package org.phantom.securityframework.auth.realm;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.subject.PrincipalCollection;
import org.phantom.securityframework.api.Account;
import org.phantom.securityframework.api.Permission;
import org.phantom.securityframework.api.AccountManager.AuthStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.phantom.securityframework.api.domain.Role;
import org.phantom.securityframework.api.exception.AccountNotActivatedException;
import org.phantom.securityframework.api.factory.ManagerFactory;
import org.phantom.securityframework.api.subject.AccountToken;
import org.phantom.securityframework.auth.realm.DefaultAuthRealm;
import org.phantom.securityframework.auth.realm.SubjectPermissionRealm;
import org.phantom.securityframework.auth.realm.SubjectRealm;

/**
 * Account认证/鉴权实现
 * @author david
 * @version 1.0.0.SNAPSHOT
 *
 */
public class AccountAuthRealm extends DefaultAuthRealm{

	private static final Logger logger = LoggerFactory.getLogger(AccountAuthRealm.class);

	public AccountAuthRealm() {
		super();
		this.setAuthenticationTokenClass(AccountToken.class);
	}

	/**
	 * 鉴权。将当前subject的所有权限注册到Shiro
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }
        String accountId = (String) getAvailablePrincipal(principals);
        
        Set<String> roleNames = new HashSet<String>();
        Set<String> permissions = new HashSet<String>();
        try {
        	
        	ManagerFactory factory = this.buildManagerFactory();
        	

        	SubjectRealm accountRealm = this.buildSubjectRealm(factory);
        	
        	if(accountRealm==null){
        		throw new Exception("accountRealm is null!");
        	}
        	Account account = (Account) accountRealm.getSubject(accountId);
        	
        	
        	for (Role r : account.getRoles()) {
				roleNames.add(r.getRoleName());
			}

        	SubjectPermissionRealm[] subjectPermissionRealms = this.buildSubjectPermissionRealms(factory);
        	
        	if(subjectPermissionRealms==null || 0==subjectPermissionRealms.length){
        		logger.warn("No subjectPermissionRealm register,subject permissions unknow!");
        	}
        	
    		List<Permission> allPermission = new ArrayList<Permission>();
    		for (SubjectPermissionRealm s : subjectPermissionRealms) {
				allPermission.addAll(s.getSubjectPermission(account));
			}
        	
        	for (Permission p : allPermission) {
    			permissions.add(p.getPermissionName().toString());
    		}

        } catch (Exception e) {
            final String message = String.format("There was a SQL error while authorizing account [%s]",accountId);
            if (logger.isErrorEnabled()) {
                logger.error(message, e);
            }

            throw new AuthorizationException(message, e);
        }

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
        info.setStringPermissions(permissions);
        return info;
	}
	
	
	
	/**
	 * 认证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException{
		try {
				
			AccountToken token = (AccountToken) authenticationToken;
			String accountId = token.getAccount();
	
	        if (accountId == null) {
	            throw new AccountException("Null account are not allowed by this realm.");
	        }
	        
	        
        	ManagerFactory factory = this.buildManagerFactory();
			
			SubjectRealm accountRealm = this.buildSubjectRealm(factory);
			
			if(accountRealm == null){
	    		throw new ConfigurationException("SubjectRealm is null!");
			}
	    	Account account = (Account) accountRealm.getSubject(accountId);
	    	if (account == null) {
	    		throw new UnknownAccountException(String.format("No account found for account [%s]",accountId));
	    	}
	    	String password = account.getPassword();
	    	String inputPassword = token.getPassword();
	    	if(password!=null){
	    		if(!password.equals(inputPassword)){
	    			throw new AuthenticationException(String.format("input password don't match for account [%s]",accountId));
	    		}
	    	}
	    	if(account.getStatus()==AuthStatus.getIndex(AuthStatus.UNACTIVATION)){
	    		throw new AccountNotActivatedException();
	    	}
	    	
//	    	accountRealm.recordLogin(account);
	        AuthenticationInfo info = new SimpleAuthenticationInfo(accountId, password, getName());
	
	        return info;

		} catch (Exception e) {
			throw new AuthenticationException(e);
		}
	}
}